/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var NORMAL_STROKE = 2
var SEQUENCEFLOW_STROKE = 2
var ASSOCIATION_STROKE = 2
var TASK_STROKE = 2
var TASK_HIGHLIGHT_STROKE = 4
var CALL_ACTIVITY_STROKE = 4
var ENDEVENT_STROKE = 4

var COMPLETED_COLOR = '#2632aa'
var TEXT_COLOR = '#373e48'
var CURRENT_COLOR = '#017501'
var HOVER_COLOR = '#666666'
var ACTIVITY_STROKE_COLOR = '#000000'
var ACTIVITY_FILL_COLOR = '#ffffff'
var MAIN_STROKE_COLOR = '#585858'

var TEXT_PADDING = 3
var ARROW_WIDTH = 4
var MARKER_WIDTH = 12

var TASK_FONT = { font: '11px Arial', opacity: 1, fill: Raphael.rgb(0, 0, 0) }

// icons
var ICON_SIZE = 16
var ICON_PADDING = 4

var INITIAL_CANVAS_WIDTH
var INITIAL_CANVAS_HEIGHT

var paper
var viewBox
var viewBoxWidth
var viewBoxHeight

var canvasWidth
var canvasHeight

var modelDiv = jQuery('#bpmnModel')
var modelId = modelDiv.attr('data-model-id')
var historyModelId = modelDiv.attr('data-history-id')
var processDefinitionId = modelDiv.attr('data-process-definition-id')
var modelType = modelDiv.attr('data-model-type')
var isDebuggerEnabled = angular.element(document.querySelector('#bpmnModel')).scope().model.isDebuggerEnabled

// Support for custom background colors for activities
var customActivityColors = modelDiv.attr('data-activity-color-mapping')
if (customActivityColors !== null && customActivityColors !== undefined && customActivityColors.length > 0) {
  // Stored on the attribute as a string
  customActivityColors = JSON.parse(customActivityColors)
}

var customActivityToolTips = modelDiv.attr('data-activity-tooltips')
if (customActivityToolTips !== null && customActivityToolTips !== undefined && customActivityToolTips.length > 0) {
  // Stored on the attribute as a string
  customActivityToolTips = JSON.parse(customActivityToolTips)
}

// Support for custom opacity for activity backgrounds
var customActivityBackgroundOpacity = modelDiv.attr('data-activity-opacity')

var elementsAdded = new Array()
var elementsRemoved = new Array()
var selectedElement = undefined

var collapsedItemNavigation = new Array()
var bpmnData

function _showTip(htmlNode, element) {
  // Custom tooltip
  var documentation = undefined
  if (customActivityToolTips) {
    if (customActivityToolTips[element.name]) {
      documentation = customActivityToolTips[element.name]
    } else if (customActivityToolTips[element.id]) {
      documentation = customActivityToolTips[element.id]
    } else {
      documentation = '' // Show nothing if custom tool tips are enabled
    }
  }

  // Default tooltip, no custom tool tip set
  if (documentation === undefined) {
    var documentation = ''
    if (element.name && element.name.length > 0) {
      documentation += '<b>Name</b>: <i>' + element.name + '</i><br/><br/>'
    }

    if (element.properties) {
      for (var i = 0; i < element.properties.length; i++) {
        var propName = element.properties[i].name
        if (element.properties[i].type && element.properties[i].type === 'list') {
          documentation += '<b>' + propName + '</b>:<br/>'
          for (var j = 0; j < element.properties[i].value.length; j++) {
            documentation += '<i>' + element.properties[i].value[j] + '</i><br/>'
          }
        } else {
          documentation += '<b>' + propName + '</b>: <i>' + element.properties[i].value + '</i><br/>'
        }
      }
    }
  }

  var text = element.type + ' '
  if (element.name && element.name.length > 0) {
    text += element.name
  } else {
    text += element.id
  }

  htmlNode.qtip({
    content: {
      text: documentation,
      title: {
        text: text
      }
    },
    position: {
      my: 'top left',
      at: 'bottom center',
      viewport: jQuery('#bpmnModel')
    },
    hide: {
      fixed: true,
      delay: 500,
      event: 'click mouseleave'
    },
    style: {
      classes: 'ui-tooltip-kisbpm-bpmn'
    }
  })
}

function _expandCollapsedElement(element) {
  if (bpmnData.collapsed) {
    for (var i = 0; i < bpmnData.collapsed.length; i++) {
      var collapsedItem = bpmnData.collapsed[i]
      if (element.id == collapsedItem.id) {
        paper.clear()

        var modelElements = collapsedItem.elements
        for (var i = 0; i < modelElements.length; i++) {
          var subElement = modelElements[i]
          var drawFunction = eval('_draw' + subElement.type)
          drawFunction(subElement)
        }

        if (collapsedItem.flows) {
          for (var i = 0; i < collapsedItem.flows.length; i++) {
            var subFlow = collapsedItem.flows[i]
            _drawFlow(subFlow)
          }
        }

        var collapsedName
        if (element.name) {
          collapsedName = element.name
        } else {
          collapsedName = 'sub process ' + element.id
        }

        collapsedItemNavigation.push({
          id: element.id,
          name: collapsedName
        })

        _buildNavigationTree()

        break
      }
    }
  }
}

function _navigateTo(elementId) {
  var modelElements = undefined
  var modelFlows = undefined
  newCollapsedItemNavigation = new Array()

  if (elementId == 'FLOWABLE_ROOT_PROCESS') {
    modelElements = bpmnData.elements
    modelFlows = bpmnData.flows
  } else {
    for (var i = 0; i < bpmnData.collapsed.length; i++) {
      var collapsedItem = bpmnData.collapsed[i]

      var collapsedName = undefined
      for (var j = 0; j < collapsedItemNavigation.length; j++) {
        if (elementId == collapsedItemNavigation[j].id) {
          collapsedName = collapsedItemNavigation[j].name
          break
        }
      }

      if (!collapsedName) {
        continue
      }

      newCollapsedItemNavigation.push({
        id: collapsedItem.id,
        name: collapsedName
      })

      if (elementId == collapsedItem.id) {
        modelElements = collapsedItem.elements
        modelFlows = collapsedItem.flows
        break
      }
    }
  }

  if (modelElements) {
    paper.clear()

    for (var i = 0; i < modelElements.length; i++) {
      var subElement = modelElements[i]
      var drawFunction = eval('_draw' + subElement.type)
      drawFunction(subElement)
    }

    if (modelFlows) {
      for (var i = 0; i < modelFlows.length; i++) {
        var subFlow = modelFlows[i]
        _drawFlow(subFlow)
      }
    }

    collapsedItemNavigation = newCollapsedItemNavigation

    _buildNavigationTree()
  }
}

function _buildNavigationTree() {
  var navigationUrl = '| <a href="javascript:_navigateTo(\'FLOWABLE_ROOT_PROCESS\')">Root</a>'

  for (var i = 0; i < collapsedItemNavigation.length; i++) {
    navigationUrl +=
      ' > <a href="javascript:_navigateTo(\'' +
      collapsedItemNavigation[i].id +
      '\')">' +
      collapsedItemNavigation[i].name +
      '</a>'
  }

  $('#navigationTree').html(navigationUrl)
}

function _addHoverLogic(element, type, defaultColor) {
  var strokeColor = _bpmnGetColor(element, defaultColor)
  var topBodyRect = null
  if (type === 'rect') {
    topBodyRect = paper.rect(element.x, element.y, element.width, element.height)
  } else if (type === 'circle') {
    var x = element.x + element.width / 2
    var y = element.y + element.height / 2
    topBodyRect = paper.circle(x, y, 15)
  } else if (type === 'rhombus') {
    topBodyRect = paper.path(
      'M' +
        element.x +
        ' ' +
        (element.y + element.height / 2) +
        'L' +
        (element.x + element.width / 2) +
        ' ' +
        (element.y + element.height) +
        'L' +
        (element.x + element.width) +
        ' ' +
        (element.y + element.height / 2) +
        'L' +
        (element.x + element.width / 2) +
        ' ' +
        element.y +
        'z'
    )
  }

  var opacity = 0
  var fillColor = '#ffffff'
  if (jQuery.inArray(element.id, elementsAdded) >= 0) {
    opacity = 0.2
    fillColor = 'green'
  }

  if (jQuery.inArray(element.id, elementsRemoved) >= 0) {
    opacity = 0.2
    fillColor = 'red'
  }

  topBodyRect.attr({
    opacity: opacity,
    stroke: 'none',
    fill: fillColor
  })
  _showTip(jQuery(topBodyRect.node), element)

  topBodyRect.mouseover(function() {
    paper.getById(element.id).attr({ stroke: HOVER_COLOR })
  })

  topBodyRect.mouseout(function() {
    paper.getById(element.id).attr({ stroke: _bpmnGetColor(element, defaultColor) })
  })

  if (isDebuggerEnabled) {
    if (element.current || element.brokenExecutions) {
      topBodyRect.click(function() {
        if (selectedElement != element.id) {
          paper.getById(element.id).attr({ stroke: 'green' })
          selectedElement = element.id
          paper.getById(element.id).attr({ stroke: 'red' })
          _executionClicked(element.id)
        } else {
          selectedElement = undefined
          paper.getById(element.id).attr({ stroke: 'green' })
          var scope = angular.element(document.querySelector('#bpmnModel')).scope()
          modelDiv.attr('selected-execution', scope.model.processInstance.id)
          scope.model.selectedExecution = scope.model.processInstance.id
          angular
            .element(document.querySelector('#variablesUi'))
            .scope()
            .loadVariables()
        }
      })
    }
  }
}

function _executionClicked(activityId) {
  var executions = angular.element(document.querySelector('#bpmnModel')).scope().model.executions
  for (var i in executions) {
    if (executions[i]['activityId'] == activityId) {
      var activityToUnselect = modelDiv.attr('selected-activity')
      if (activityToUnselect) {
        var rectangleToUnselect = paper.getById(activityToUnselect)
        if (rectangleToUnselect) {
          rectangleToUnselect.attr({ stroke: 'green' })
        }
      }
      modelDiv.attr('selected-execution', executions[i].id)
      modelDiv.attr('selected-activity', activityId)
      if (activityId) {
        paper.getById(activityId).attr({ stroke: 'red' })
      }

      var scope = angular.element(document.querySelector('#bpmnModel')).scope()
      if (scope.gridExecutions.data) {
        for (var j = 0; j < scope.gridExecutions.data.length; j++) {
          if (executions[i].id == scope.gridExecutions.data[j].id) {
            scope.gridExecutionsApi.selection.selectRow(scope.gridExecutions.data[j])
            j = scope.gridExecutions.data.length
          }
        }
      }
      scope.loadVariables()
      return
    }
  }
}

function _breakpointRestCall(actionType, activityId) {
  $.ajax({
    type: actionType,
    url: FLOWABLE.CONFIG.contextRoot + '/app/rest/debugger/breakpoints',
    contentType: 'application/json; charset=utf-8',
    data: JSON.stringify({
      activityId: activityId
    }),
    success: function() {
      paper.clear()
      angular
        .element(document.querySelector('#bpmnModel'))
        .scope()
        .getEventLog()
      _showProcessDiagram()
    }
  })
}

function _drawBreakpoint(element, breakpoints) {
  var circle = paper.circle(element.x + 10, element.y - 10, 7)

  var breakpointFillColor = 'white'
  var breakpointStrokeColor = 'gray'
  var breakpointTipText = 'Inactive element'
  if (element.current) {
    breakpointFillColor = 'red'
    breakpointTipText = 'Active execution'
  }

  if (element.breakpoint) {
    breakpointTipText = breakpointTipText + '<br/> Click to remove breakpoint'
    breakpointStrokeColor = 'red'
    circle.click(function() {
      _breakpointRestCall('DELETE', element.id)
    })
  } else {
    breakpointTipText = breakpointTipText + '<br/> Click to add breakpoint'
    circle.click(function() {
      _breakpointRestCall('POST', element.id)
    })
  }

  circle.attr('stroke', breakpointStrokeColor)
  circle.attr('stroke-width', '3')
  circle.attr('fill', breakpointFillColor)

  var circleHtmlNode = jQuery(circle.node)
  circleHtmlNode.qtip({
    content: {
      text: breakpointTipText,
      button: false
    },
    position: {
      my: 'top left',
      at: 'bottom center',
      viewport: jQuery('#bpmnModel')
    },
    hide: {
      fixed: true,
      delay: 500,
      event: 'click mouseleave'
    },
    style: {
      classes: 'ui-tooltip-kisbpm-bpmn'
    }
  })
}

function _zoom(zoomIn) {
  var tmpCanvasWidth, tmpCanvasHeight
  if (zoomIn) {
    tmpCanvasWidth = canvasWidth * (1.0 / 0.9)
    tmpCanvasHeight = canvasHeight * (1.0 / 0.9)
  } else {
    tmpCanvasWidth = canvasWidth * (1.0 / 1.1)
    tmpCanvasHeight = canvasHeight * (1.0 / 1.1)
  }

  if (tmpCanvasWidth != canvasWidth || tmpCanvasHeight != canvasHeight) {
    canvasWidth = tmpCanvasWidth
    canvasHeight = tmpCanvasHeight
    paper.setSize(canvasWidth, canvasHeight)
  }
}

var modelUrl
if (modelType == 'runtime') {
  if (isDebuggerEnabled) {
    modelUrl = FLOWABLE.CONFIG.contextRoot + '/app/rest/process-instances/debugger/' + modelId + '/model-json'
  } else {
    modelUrl = FLOWABLE.CONFIG.contextRoot + '/app/rest/display/process-instances/history/' + modelId + '/model-json'
  }
} else if (modelType == 'design') {
  if (historyModelId) {
    modelUrl =
      FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/history/' + historyModelId + '/model-json'
  } else {
    modelUrl = FLOWABLE.CONFIG.contextRoot + '/app/rest/models/' + modelId + '/model-json'
  }
} else if (modelType == 'process-definition') {
  modelUrl = FLOWABLE.CONFIG.contextRoot + '/app/rest/process-definitions/' + processDefinitionId + '/model-json'
}

function _drawContinueExecution(x, y, executionId, activityId) {
  var arrow = paper.path('M ' + x + ' ' + y + ' L ' + (x + 8) + ' ' + (y + 4) + ' ' + x + ' ' + (y + 8) + ' z')

  arrow.click(function() {
    $.ajax({
      type: 'PUT',
      url: FLOWABLE.CONFIG.contextRoot + '/app/rest/debugger/breakpoints/' + executionId + '/continue',
      contentType: 'application/json; charset=utf-8',
      success: function() {
        paper.clear()
        var processInstanceId = angular.element(document.querySelector('#bpmnModel')).scope().model.processInstance.id
        modelDiv.attr('selected-execution', processInstanceId)
        angular.element(document.querySelector('#bpmnModel')).scope().model.selectedExecution = processInstanceId
        angular
          .element(document.querySelector('#bpmnModel'))
          .scope()
          .getExecutions()
        angular.element(document.querySelector('#bpmnModel')).scope().model.variables = undefined
        angular
          .element(document.querySelector('#bpmnModel'))
          .scope()
          .loadVariables()
        angular
          .element(document.querySelector('#bpmnModel'))
          .scope()
          .getEventLog()
        _showProcessDiagram()
      }
      // error: function() {
      //   alert('error')
      // }
    })
  })
  arrow.attr('stroke', 'green')
  arrow.attr('stroke-width', '3')
  arrow.attr('fill', 'green')

  var arrowHtmlNode = jQuery(arrow.node)
  arrowHtmlNode.qtip({
    content: {
      text: 'Fire execution ' + executionId + ', activity ' + activityId,
      button: false
    },
    position: {
      my: 'top left',
      at: 'bottom center',
      viewport: jQuery('#bpmnModel')
    },
    hide: {
      fixed: true,
      delay: 500,
      event: 'click mouseleave'
    },
    style: {
      classes: 'ui-tooltip-kisbpm-bpmn'
    }
  })
}

function _showProcessDiagram() {
  var request = jQuery.ajax({
    type: 'get',
    url: modelUrl + '?nocaching=' + new Date().getTime()
  })

  request.success(function(data, textStatus, jqXHR) {
    if ((!data.elements || data.elements.length == 0) && (!data.pools || data.pools.length == 0)) return

    INITIAL_CANVAS_WIDTH = data.diagramWidth

    if (modelType == 'design') {
      INITIAL_CANVAS_WIDTH += 20
    } else {
      INITIAL_CANVAS_WIDTH += 30
    }

    INITIAL_CANVAS_HEIGHT = data.diagramHeight + 50
    canvasWidth = INITIAL_CANVAS_WIDTH
    canvasHeight = INITIAL_CANVAS_HEIGHT
    viewBoxWidth = INITIAL_CANVAS_WIDTH
    viewBoxHeight = INITIAL_CANVAS_HEIGHT

    if (modelType == 'design') {
      var headerBarHeight = 170
      var offsetY = 0
      if (jQuery(window).height() > canvasHeight + headerBarHeight) {
        offsetY = (jQuery(window).height() - headerBarHeight - canvasHeight) / 2
      }

      if (offsetY > 50) {
        offsetY = 50
      }

      jQuery('#bpmnModel').css('marginTop', offsetY)
    }

    jQuery('#bpmnModel').width(INITIAL_CANVAS_WIDTH)
    jQuery('#bpmnModel').height(INITIAL_CANVAS_HEIGHT)
    paper = Raphael(document.getElementById('bpmnModel'), canvasWidth, canvasHeight)
    paper.setViewBox(0, 0, viewBoxWidth, viewBoxHeight, false)
    paper.renderfix()

    if (data.pools) {
      for (var i = 0; i < data.pools.length; i++) {
        var pool = data.pools[i]
        _drawPool(pool)
      }
    }

    var modelElements = data.elements

    for (var i = 0; i < modelElements.length; i++) {
      var element = modelElements[i]
      //            try {
      var drawFunction = eval('_draw' + element.type)
      drawFunction(element)
      if (isDebuggerEnabled) {
        _drawBreakpoint(element)

        if (element.brokenExecutions) {
          for (var j = 0; j < element.brokenExecutions.length; j++) {
            _drawContinueExecution(element.x + 25 + j * 10, element.y - 15, element.brokenExecutions[j], element.id)
          }
        }
      }
      //            } catch(err) {console.log(err);}
    }

    if (data.flows) {
      for (var i = 0; i < data.flows.length; i++) {
        var flow = data.flows[i]
        if (flow.type === 'sequenceFlow') {
          _drawFlow(flow)
        } else if (flow.type === 'association') {
          _drawAssociation(flow)
        }
      }
    }

    bpmnData = data
  })

  // request.error(function(jqXHR, textStatus, errorThrown) {
  //   alert('error1')
  // })
}

_showProcessDiagram()
